package data.input;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.joda.time.LocalDate;

import data.enums.Classification;
import data.enums.Index;
import entities.IndexTuple;

public abstract class Data implements Serializable {

	private static final long serialVersionUID = 4173618016148460318L;

	protected Index indexName;

	protected Map<LocalDate, IndexTuple> tuplesMap = new HashMap<>();

	abstract protected void initializeTuplesMapWithoutClassification(Index indexName) throws FileNotFoundException, IOException;

	public static Data createData(Index index) throws IOException, ParseException {

		if (index == Index.Stockholm_General || index == Index.Dow_Jones) {
			return new WallStreetJournalCsv(index);
		} else {
			return new YahooCsv(index);
		}
	}
	
	protected void initializeTuplesMap(Index indexName) throws FileNotFoundException, IOException {
		
		this.indexName = indexName;

		initializeTuplesMapWithoutClassification(indexName);

		setAllClassifications();
	}

	/**
	 * this method is necessary when data in csv or in another extension does not have a
	 * diference betwen a day and the day before. 
	 */
	protected void setAllClassifications() {

		LocalDate dateOfTupleToBeRemoved = null;

		for (IndexTuple tuple : tuplesMap.values()) {

			LocalDate date = tuple.getDate();
			BigDecimal close = tuplesMap.get(date).getClose();

			BigDecimal previousClose = getPreviousClose(date);

			if (previousClose == null) {
				dateOfTupleToBeRemoved = tuple.getDate();
			} else if (close.compareTo(previousClose) > 0) {
				tuple.setClassification(Classification.HIGH);
			} else {
				tuple.setClassification(Classification.LOW);
			}

		}

		tuplesMap.remove(dateOfTupleToBeRemoved);
	}

	/**
	 * return null if the Date pass as argument is the first date
	 */
	public BigDecimal getPreviousClose(LocalDate date) {

		IndexTuple previousTuple = getPreviousIndexTuple(date);

		if (previousTuple != null) {
			return previousTuple.getClose();
		} else {
			return null;
		}

	}

	/**
	 * return null if the Date pass as argument is the first date
	 */
	public IndexTuple getPreviousIndexTuple(LocalDate date) {

		IndexTuple previousTuple = null;
		LocalDate current = date;

		for (int i = 0; i < 100; i++) {

			LocalDate previousDate = current.minusDays(1);

			previousTuple = tuplesMap.get(previousDate);

			if (previousTuple != null) {
				break;
			}

			current = previousDate;

		}

		return previousTuple;

	}

	public IndexTuple getIndexTuple(LocalDate date) {
		return this.tuplesMap.get(date);
	}

	public Collection<IndexTuple> getIndexTuples() {
		return this.tuplesMap.values();
	}

	public Index getIndexName() {
		return indexName;
	}

}
